//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
// Unit test code for the RegisterXRObject functionality in Alchemy
//
#pragma once
#include "Interactions.h"

#include <XRUnknown.h>
#include <XRDelegate.h>

//
// define a max string length for Interactions
//
#define MAX_INTERACTION_STRING_LENGTH 512


//
// templated base class for IAttachedObjects
//
//    ConstraintType : specifies the only type of object this can 
//    be attached to
//
//    IFace : specifies the base interface of this object
//
template <typename ConstraintType, typename IFace>
class XRAttachedObject : public IFace
{
    BEGIN_QI_MAP()
        QI_MAP_INTERFACE(IXRAttachedObject)
        QI_MAP_INTERFACE(IFace)
    END_QI_MAP()

public:
    /// <summary>
    /// Creation method for a given implementation, specified with the template
    /// argument Impl, of the XRAttachedObject.
    /// </summary>
    /// <param name="pDependencyObject">Core representation of this XRAttachedObject</param>
    /// <param name="ppAttachedObject">XRAttachedObject that is created</param>
    template <typename Impl>
    static HRESULT Create(
        __in IXRDependencyObject *pDependencyObject, 
        __out IXRAttachedObject** ppAttachedObject)
    {
        XRPtr<Impl, IXRAttachedObject> pAttachedObject;
        HRESULT hr = S_OK;

        if (NULL == pDependencyObject)
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        if (NULL == ppAttachedObject)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        hr = XRObject<Impl>::CreateInstance(&pAttachedObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        if (NULL == pAttachedObject)
        {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        hr = pAttachedObject->SetAttachedDependencyObject(pDependencyObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        *ppAttachedObject = pAttachedObject.Detach();

    Error:
        return hr;
    }

    virtual ~XRAttachedObject() 
    {
        if (m_pWeakReferenceTargetObject && m_pAttachedObjectDestructionDelegate)
        {
            m_pWeakReferenceTargetObject->RemoveDestroyedEventHandler(m_pAttachedObjectDestructionDelegate);
        }

        m_pWeakReferenceTargetObject = NULL;
        m_pAttachedObjectDestructionDelegate = NULL;
    }

    /// <summary>
    /// Method called to attach an object
    /// </summary>
    /// <param name="pTargetObject">Object to attach</param>
    virtual HRESULT STDMETHODCALLTYPE OnAttached(
        __in IXRDependencyObject *pTargetObject)
    {
        return SetTargetObject(pTargetObject);
    }

    /// <summary>
    /// Method called detach any objects
    /// </summary>
    virtual HRESULT STDMETHODCALLTYPE OnDetaching(void)
    {
        return SetTargetObject(NULL);
    }

    /// <summary>
    /// Retrieve the IXRAttachedObject's dependency object
    /// </summary>
    /// <param name="ppAttachedDependencyObject">IXRDependencyObject to store the associated dependency object</param>
    HRESULT STDMETHODCALLTYPE GetAttachedDependencyObject(
        __out IXRDependencyObject **ppAttachedDependencyObject)
    {
        HRESULT hr = S_OK;

        if (NULL == ppAttachedDependencyObject)
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        *ppAttachedDependencyObject = NULL;
        if (NULL == m_pAttachedDependencyObject)
        {
            hr = E_UNEXPECTED;
            goto Error;
        }
        m_pAttachedDependencyObject.QueryInterface(ppAttachedDependencyObject);

    Error:
        return hr;
    }

    /// <summary>
    /// Retrieve the IXRAttachedObject's target object
    /// </summary>
    /// <param name="ppAttachedDependencyObject">IXRDependencyObject to store the associated dependency object</param>
    HRESULT STDMETHODCALLTYPE GetTargetDependencyObject(
        __out IXRDependencyObject **ppTargetObject)
    {
        HRESULT hr = S_OK;

        if (NULL == ppTargetObject)
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        *ppTargetObject = NULL;
        if (m_pWeakReferenceTargetObject)
        {
            XRPtr<ConstraintType> pContraint = m_pWeakReferenceTargetObject;
            *ppTargetObject = pContraint.Detach();
        }

    Error:
        return hr;
    }

protected:
    XRAttachedObject() : m_pWeakReferenceTargetObject(NULL)
    {
    }

    /// <summary>
    /// Utility method called to get a dependency property
    /// </summary>
    /// <param name="id">DependencyProperty id</param>
    /// <param name="pValue">DependencyProperty value</param>
    template <typename Value>
    HRESULT GetDependencyProperty(
        __in DEPENDENCY_PROPERTY_ID id, 
        __out Value* pValue)
    {
        XRPtr<IXRDependencyObject> pAttachedDependencyObject;
        HRESULT hr = S_OK;

        hr = GetAttachedDependencyObject(&pAttachedDependencyObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = pAttachedDependencyObject->GetDependencyProperty(id, pValue);
        if (FAILED(hr))
        {
            goto Error;
        }

    Error:
        return hr;
    }
    /// <summary>
    /// Utility method called to set a dependency property
    /// </summary>
    /// <param name="id">DependencyProperty id</param>
    /// <param name="pValue">DependencyProperty value</param>
    template <typename Value>
    HRESULT SetDependencyProperty(
        __in DEPENDENCY_PROPERTY_ID id, 
        __in Value pValue)
    {
        XRPtr<IXRDependencyObject> pAttachedDependencyObject;
        HRESULT hr = S_OK;

        hr = GetAttachedDependencyObject(&pAttachedDependencyObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = pAttachedDependencyObject->SetDependencyProperty(id, pValue);
        if (FAILED(hr))
        {
            goto Error;
        }

    Error:
        return hr;
    }

    /// <summary>
    /// Method called to set the current target object
    /// </summary>
    /// <param name="pTargetObject">Object that this IXRAttachedObject 
    /// is attached to</param>
    HRESULT SetTargetObject(
        __in IXRDependencyObject *pTargetObject)
    {
        XRPtr<IXRDependencyObject> pOldTargetObject;
        HRESULT hr = S_OK;

        EnsureAttachedObjectDestructionDelegate();

        // remove destruction event handler from the old target object
        // if there is one
        hr = GetTargetDependencyObject(&pOldTargetObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        if (pOldTargetObject)
        {
            pOldTargetObject->RemoveDestroyedEventHandler(m_pAttachedObjectDestructionDelegate);
        }

        if (pTargetObject)
        {
            XRPtr<IXRInteraction> pInteractions;
            XRPtr<ConstraintType> pTestObject;

            // validate the object based on the ConstraintType using XRPtr's implicit QI
            pTestObject = pTargetObject;
            if (NULL == pTestObject)
            {
                hr = E_FAIL;
                goto Error;
            }

            // if the ConstraintType is validated, set the target object's WEAK reference
            m_pWeakReferenceTargetObject = pTargetObject;
            // add destruction event handler to the target object so that the WEAK reference
            // can be NULL-ified when it is invalid
            m_pWeakReferenceTargetObject->AddDestroyedEventHandler(m_pAttachedObjectDestructionDelegate);

            // ensure that this IXRAttachedObject is cached in IXRInteraction
            hr = GetInteractionsInstance(&pInteractions);
            if (FAILED(hr))
            {
                goto Error;
            }
            hr = pInteractions->CacheAttachedObject(this);
            if (FAILED(hr))
            {
                goto Error;
            }
        }
        else
        {
            m_pWeakReferenceTargetObject = NULL;
        }

    Error:
        return hr;
    }

    /// <summary>
    /// Method called to set the attached object's dependency object
    /// </summary>
    /// <param name="pAttachedDependencyObject">IXRDependencyObject</param>
    HRESULT SetAttachedDependencyObject(
        __in IXRDependencyObject *pAttachedDependencyObject)
    {
        HRESULT hr = S_OK;
        m_pAttachedDependencyObject = pAttachedDependencyObject;
        return hr;
    }

private:
    /// <summary>
    /// Method called to ensure that this AttachedObject's delegate has been created
    /// </summary>
    void EnsureAttachedObjectDestructionDelegate()
    {
        if (NULL == m_pAttachedObjectDestructionDelegate)
        {
            m_pAttachedObjectDestructionDelegate = CreateDelegate(this, &XRAttachedObject::HandleAttachedObjectDestruction);
        }
    }

    /// <summary>
    /// Method used to handle AttachedDependencyObject's Destroyed event.  This event
    /// will enable the AttachedObject to remove itself from the IXRInteraction IXRAttachedObject
    /// cache.
    /// </summary>
    /// <param name="pSender">IXRDependencyObject that is being destroyed</param>
    /// <param name="pArgs">XREventArgs</param>
    HRESULT HandleAttachedObjectDestruction(
        IXRDependencyObject *pSender,
        XREventArgs *pArgs)
    {
        HRESULT hr = S_OK;

        XRPtr<IXRInteraction> pInteractions;

        // destroy the target object pointer!
        m_pWeakReferenceTargetObject = NULL;

        // remove this attached object from the Interactions' cache
        hr = GetInteractionsInstance(&pInteractions);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = pInteractions->RemoveCachedAttachedObject(this);
        if (FAILED(hr))
        {
            goto Error;
        }

    Error:
        return hr;
    }

private:
    XRPtr<IXRDependencyObject> m_pAttachedDependencyObject;

    XRPtr<IXRDelegate<XREventArgs>> m_pAttachedObjectDestructionDelegate;
    // this is a WEAK reference.  the destruction event handler will need to NULL-ify this
    // pointer when the reference object is destroyed!!!
    IXRDependencyObject* m_pWeakReferenceTargetObject;
};


//
// templated base class for IBehaviors
//
template <typename BehaviorImpl, typename IFace, typename ConstraintType = IXRFrameworkElement>
class XRBehavior : 
    public XRAttachedObject<ConstraintType, IFace>
{
    BEGIN_QI_MAP()
        QI_MAP_CHAIN(XRAttachedObject)
        QI_MAP_INTERFACE(BehaviorImpl)
        QI_MAP_INTERFACE(IXRBehavior)
    END_QI_MAP()

public:
    /// <summary>
    /// Method used to create a new Behavior
    /// </summary>
    /// <param name="pAttachedDependencyObject">IXRDependencyObject containing corresponding XamlRuntime object</param>
    /// <param name="ppAttachedObject">IXRAttachedObject storing the newly created object</param>
    static HRESULT CALLBACK Create(
        __in IXRDependencyObject *pAttachedDependencyObject, 
        __out IXRAttachedObject **ppAttachedObject)
    {
        return XRAttachedObject::Create<BehaviorImpl>(pAttachedDependencyObject, ppAttachedObject);
    }

    /// <summary>
    /// Method used to register a new Behavior
    /// </summary>
    /// <param name="pBehaviorName">string containing the XAML name of this behavior</param>
    /// <param name="pBehaviorNamespace">string containing the namespace of this behavior</param>
    static HRESULT Register(
        __in const WCHAR *pBehaviorName, 
        __in const WCHAR *pBehaviorNamespace)
    {
        XRPtr<IXRInteraction> pInteractions;
        HRESULT hr = S_OK;

        hr = GetInteractionsInstance(&pInteractions);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = pInteractions->RegisterAttachedObject(
                __uuidof(IFace), 
                pBehaviorName, pBehaviorNamespace,
                Create, &s_BehaviorId);
        if (FAILED(hr))
        {
            goto Error;
        }

    Error:
        return hr;
    }

protected:
    static UINT s_BehaviorId;
};
template <typename BehaviorImpl, typename IFace, typename ConstraintType>
UINT XRBehavior<BehaviorImpl, IFace, ConstraintType>::s_BehaviorId = 0;


//
// templated base class for IXRInteractivityTriggerBase
//
template <typename TriggerImpl, typename IFace, typename ConstraintType = IXRDependencyObject>
class XREventTriggerBase : 
    public XRAttachedObject<ConstraintType, IFace>
{
    BEGIN_QI_MAP()
        QI_MAP_CHAIN(XRAttachedObject)
        QI_MAP_INTERFACE(TriggerImpl)
        QI_MAP_INTERFACE(IXRInteractivityTriggerBase)
    END_QI_MAP()

public:
    /// <summary>
    /// Method used to set an event name
    /// </summary>
    /// <param name="pEventName">string containing new event name</param>
    virtual HRESULT STDMETHODCALLTYPE SetEventName(__in const WCHAR* pEventName)
    {
        HRESULT hr = S_OK;
        size_t length = 0;

        if (pEventName)
        {
            hr = StringCchLength(pEventName, MAX_INTERACTION_STRING_LENGTH, &length);
            if (FAILED(hr))
            {
                goto Error;
            }
            if (0 >= length)
            {
                hr = E_INVALIDARG;
                goto Error;
            }
        }
        hr = XRAttachedObject::SetDependencyProperty(s_eventNameId, pEventName);

    Error:
        return hr;
    }
    /// <summary>
    /// Method used to get an event name
    /// </summary>
    /// <param name="pEventName">string used to store event name</param>
    virtual HRESULT STDMETHODCALLTYPE GetEventName(__out BSTR *pEventName)
    {
        HRESULT hr = S_OK;

        if (!pEventName)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        hr = XRAttachedObject::GetDependencyProperty(s_eventNameId, pEventName);
    
    Error:
        return hr;
    }
    
    /// <summary>
    /// Method used to set an source name
    /// </summary>
    /// <param name="pSourceName">string containing new source name</param>
    virtual HRESULT STDMETHODCALLTYPE SetSourceName(__in const WCHAR* pSourceName)
    {
        HRESULT hr = S_OK;
        size_t length = 0;

        if (pSourceName)
        {
            hr = StringCchLength(pSourceName, MAX_INTERACTION_STRING_LENGTH, &length);
            if (FAILED(hr))
            {
                goto Error;
            }
            if (0 >= length)
            {
                hr = E_INVALIDARG;
                goto Error;
            }
        }
        hr = XRAttachedObject::SetDependencyProperty(s_sourceNameId, pSourceName);

    Error:
        return hr;
    }
    /// <summary>
    /// Method used to get an source name
    /// </summary>
    /// <param name="pSourceName">string used to store source name</param>
    virtual HRESULT STDMETHODCALLTYPE GetSourceName(__out BSTR *pSourceName)
    {
        HRESULT hr = S_OK;

        if (!pSourceName)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        hr = XRAttachedObject::GetDependencyProperty(s_sourceNameId, pSourceName);

    Error:
        return hr;
    }

    /// <summary>
    /// Method used to set the EventTriggerAction collection
    /// </summary>
    /// <param name="pActions">Collection used to store the actions</param>
    virtual HRESULT STDMETHODCALLTYPE SetActions(__in IXRInteractivityTriggerActionCollection* pActions)
    {
        XRPtr<IXREnumerable> pEnumerable;
        HRESULT hr = S_OK;

        // if a non-null action collection is provided, ensure that it is an enumerable.
        if (pActions)
        {
            pEnumerable = pActions;
            if (!pEnumerable)
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        
        hr = XRAttachedObject::SetDependencyProperty(s_actionsId, pEnumerable);

    Error:
        return hr;
    }
    /// <summary>
    /// Method used to get the EventTriggerAction collection
    /// </summary>
    /// <param name="ppActions">Collection used to store the actions</param>
    virtual HRESULT STDMETHODCALLTYPE GetActions(__out IXRInteractivityTriggerActionCollection** ppActions)
    {
        XRPtr<IXREnumerable> pEnumerable;
        HRESULT hr = S_OK;

        if (!ppActions)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        *ppActions = NULL;

        hr = XRAttachedObject::GetDependencyProperty(s_actionsId, &pEnumerable);
        if (FAILED(hr))
        {
            goto Error;
        }

        if (NULL != pEnumerable)
        {
            hr = pEnumerable.QueryInterface(ppActions);
        }

    Error:
        return hr;
    }

public:
    /// <summary> No arg constructor </summary>
    XREventTriggerBase() : XRAttachedObject()
    {
    }

    /// <summary>destructor</summary>
    virtual ~XREventTriggerBase()
    {
    }


    /// <summary>
    /// Method used to attach to an existing object
    /// </summary>
    /// <param name="pIXRDO">object that is being attached to</param>
    virtual HRESULT STDMETHODCALLTYPE OnAttached(__in IXRDependencyObject* pIXRDO)
    {
        XRPtr<IXRDependencyObject> pOldAssociatedObject;
        XRPtr<IXRInteractivityTriggerActionCollection> pActions;
        XRPtr<IXRFrameworkElement> pIXRDOFE = pIXRDO;
        bool attachedToNewObject = false;
        HRESULT hr = S_OK;

        XREventTriggerSpecification spec;

        // this method will remove the pre-existing attached object if it exists
        // and add a new attached object.

        // 1.  get the existing event name and source name as part of the 
        //      XREventTriggerSpecification
        hr = GetEventName(&(spec.pEventName));
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = GetSourceName(&(spec.pSourceName));
        if (FAILED(hr))
        {
            goto Error;
        }

        // 2.  get the target object
        hr = XRAttachedObject::GetTargetObject(&pOldAssociatedObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        // 3.  if this is a new attached object and the old object was not null, the
        //      existing event handler needs to be removed
        attachedToNewObject = (pOldAssociatedObject != pIXRDO);
        if (attachedToNewObject && pOldAssociatedObject)
        {
            XRPtr<IXRFrameworkElement> pOldAssociatedObjectFE = pOldAssociatedObject;
            // remove the existing handler
            UnregisterHandler(&spec);
            // if the old attached object is a non-null FrameworkElement, make sure that this object's
            // loaded event handler is removed.  
            if (NULL != pOldAssociatedObjectFE)
            {
                EnsureAssociatedObjectLoadedEventHandler();
                pOldAssociatedObjectFE->RemoveLoadedEventHandler(m_pLoadedEventHandler);
            }
        }

        // 4.  attach the object and attach to the Actions object
        hr = XRAttachedObject::OnAttached(pIXRDO);
        if (FAILED(hr))
        {
            goto Error;
        }
        if (SUCCEEDED(GetActions(&pActions)))
        {
            if (pActions)
            {
                hr = pActions->OnAttached(pIXRDO);
                if (FAILED(hr))
                {
                    goto Error;
                }
            }
        }

        // 5.  if the object is different from a previous attached object, register 
        //      to handle this trigger's event
        if (attachedToNewObject)
        {
            // try to register a handler for this trigger's event
            RegisterHandler(&spec);

            // if the object represented by SourceName hasn't "entered" the tree
            // yet (as can happen during parsing, one of two things may happen:
            //    1.  FindName ( SourceName ) will fail
            //    2.  Add*EventHandler will fail but will return S_OK
            // in either case, we need to wait for the OnLoaded event in order
            // to ensure that this trigger's event has a handler successfully
            // registered.
            //
            // so, just to be safe, add an OnLoaded event handler that will 
            // call RegisterHandler.
            if (pIXRDOFE)
            {
                EnsureAssociatedObjectLoadedEventHandler();
                pIXRDOFE->AddLoadedEventHandler(m_pLoadedEventHandler);
            }
        }

    Error:
        return hr;
    }
    /// <summary>
    /// Method used to detach from an existing object
    /// </summary>
    virtual HRESULT STDMETHODCALLTYPE OnDetaching()
    {
        return OnAttached(NULL);
    }

protected:
    /// <summary>
    /// struct describing the elements that constitute an EventTrigger
    /// </summary>
    struct XREventTriggerSpecification
    {
        BSTR pEventName;
        BSTR pSourceName;

    public:
        /// <summary>
        /// Constructor ensures initialization to NULL
        /// </summary>
        XREventTriggerSpecification()
        {
            pEventName = SysAllocString(L"");
            pSourceName = SysAllocString(L"");
        }

        /// <summary>
        /// Copy Constructor ensures initialization to NULL
        /// </summary>
        XREventTriggerSpecification(const XREventTriggerSpecification& wtf)
        {
            SysFreeString(pEventName);
            pEventName = SysAllocString(wtf.pEventName);

            SysFreeString(pSourceName);
            pSourceName = SysallocString(wtf.pSourceName);
        }

        /// <summary>
        /// Constructor allows a trigger to be used as a source for 
        /// the event and source names
        /// </summary>
        /// <param name="pTrigger">IXRInteractivityTriggerBase used to initialize this specification</param>
        XREventTriggerSpecification(IXRInteractivityTriggerBase *pTrigger)
        {
            if (NULL != pTrigger)
            {
                pTrigger->GetEventName(&pEventName);
                pTrigger->GetSourceName(&pSourceName);
            }
        }
        /// <summary>
        /// Destructor deallocates any non-null BSTR's
        /// </summary>
        virtual ~XREventTriggerSpecification()
        {
            SysFreeString(pEventName);
            SysFreeString(pSourceName);
        }
    };

    /// <summary>
    /// Method called when EventTrigger handler should be registered
    /// </summary>
    /// <return>S_OK if a handler was registered, S_FALSE if nothing was 
    /// registered, E_FAIL if other failure </return>
    virtual HRESULT RegisterHandler(__in XREventTriggerSpecification *pSpecification) = 0;

    /// <summary>
    /// Method called when EventTrigger handler should be unregistered
    /// </summary>
    /// <return>S_OK if a handler was unregistered, S_FALSE if nothing was 
    /// unregistered, E_FAIL if other failure </return>
    virtual HRESULT UnregisterHandler(__in XREventTriggerSpecification *pSpecification) = 0;

protected:
    /// <summary>
    /// Method used to register a EventTriggerBase
    /// </summary>
    /// <param name="pEventTriggerName">string containing EventTrigger name
    /// as it should appear in XAML</param>
    /// <param name="pNamespace">string containing behavior namespace</param>
    static HRESULT Register(__in const WCHAR* pEventTriggerName, __in const WCHAR* pEventTriggerNamespace)
    {
        HRESULT hr = E_FAIL;
        XRPtr<IXRInteraction> pInteractions;
        XRPtr<IXRApplication> pApplication;
        XRDependencyPropertyMetaData eventMD, sourceMD;

        eventMD.pfnPropertyChangeNotification = &EventNameChanged;
        sourceMD.pfnPropertyChangeNotification = &SourceNameChanged;

        // get XR application and interactions instances
        hr = GetXRApplicationInstance(&pApplication);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = GetInteractionsInstance(&pInteractions);
        if (FAILED(hr))
        {
            goto Error;
        }

        // get XR application and register the new EventTriggerBase type
        hr = pInteractions->RegisterAttachedObject(__uuidof(IFace), pEventTriggerName, pEventTriggerNamespace, &CreateEventTrigger, &TriggerImpl::s_ControlId);
        if (FAILED(hr))
        {
            goto Error;
        }
        // add EventName property
        hr = pApplication->RegisterDependencyProperty(L"EventName", VTYPE_BSTR, TriggerImpl::s_ControlId, &eventMD, &TriggerImpl::s_eventNameId);
        if (FAILED(hr))
        {
            goto Error;
        }
        // add EventName property
        hr = pApplication->RegisterDependencyProperty(L"SourceName", VTYPE_BSTR, TriggerImpl::s_ControlId, &sourceMD, &TriggerImpl::s_sourceNameId);
        if (FAILED(hr))
        {
            goto Error;
        }
        // add Actions property
        hr = pInteractions->RegisterActionsDependencyProperty(L"Actions", TriggerImpl::s_ControlId, &ActionsChanged, &TriggerImpl::s_actionsId);

    Error:
        return hr;
    }

private:
    /// <summary>
    /// Ensure that the loaded event handler has been created.
    /// </summary>
    void EnsureAssociatedObjectLoadedEventHandler()
    {
        if (NULL == m_pLoadedEventHandler)
        {
            m_pLoadedEventHandler = CreateDelegate(this, &XREventTriggerBase::AssociatedObjectLoadedEventHandler);
        }
    }

    /// <summary>
    /// Handle the loaded event by registering a handler
    /// </summary>
    /// <param name="pIXRDO">IXRDependencyObject</param>
    /// <param name="pArgs">IXRRoutedEventArgs</param>
    HRESULT AssociatedObjectLoadedEventHandler(__in IXRDependencyObject *pIXRDO, __in XRRoutedEventArgs *pArgs)
    {
        XREventTriggerSpecification Spec;
        HRESULT hr = S_OK;

        hr = GetSourceName(&Spec.pSourceName);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = GetEventName(&Spec.pEventName);
        if (FAILED(hr))
        {
            goto Error;
        }
        UnregisterHandler(&Spec);
        hr = RegisterHandler(&Spec);

    Error:
        return hr;
    }

private:
    /// <summary>
    /// Create method used to create an instance of the EventTrigger
    /// </summary>
    /// <param name="pIXRDO">IXRDependencyObject containing bridge object</param>
    /// <param name="ppAttachedObject">IXRAttachedObject used to store trigger</param>
    static HRESULT WINAPI CreateEventTrigger(__in IXRDependencyObject* pIXRDO, __out IXRAttachedObject **ppAttachedObject)
    {
        return XRAttachedObject::Create<TriggerImpl>(pIXRDO, ppAttachedObject);
    }

    /// <summary>
    /// Method used to convert an IXRDO into an XREventTriggerBase (which has no GUID, so standard QI 
    /// won't work).  Used by the EventName and SourceName changed methods.
    /// </summary>
    /// <param name="pIXRDO">IXRDependencyObject containing bridge object</param>
    /// <param name="ppBase">XREventTriggerBase used to store trigger</param>
    static HRESULT GetXREventTriggerBase(__in IXRDependencyObject *pIXRDO, __out XREventTriggerBase **ppBase)
    {
        XRPtr<IXRInteraction> pInteractions;
        XRPtr<TriggerImpl> pEventTrigger;
        HRESULT hr = S_OK;

        if (!pIXRDO || !ppBase)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        hr = GetInteractionsInstance(&pInteractions);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = pInteractions->GetAttachedObject(pIXRDO, &pEventTrigger);
        if (FAILED(hr))
        {
            goto Error;
        }
        if (!pEventTrigger)
        {
            hr = E_FAIL;
            goto Error;
        }

        *ppBase = pEventTrigger.Detach();

    Error:
        return hr;
    }

    /// <summary>
    /// Method used to handle a change to the EventName dependency property.
    /// </summary>
    /// <param name="pIXRDO">IXRDependencyObject containing bridge object</param>
    /// <param name="pOldValue">XRValue containing the old value of EventName</param>
    /// <param name="pNewValue">XRValue containing the new value of EventName</param>
    static void CALLBACK EventNameChanged(__in IXRDependencyObject* pIXRDO, __in XRValue* pOldValue, __in XRValue* pNewValue)
    {
        XRPtr<XREventTriggerBase> pEventTriggerBase;
        XREventTriggerSpecification oldSpec, newSpec;
        HRESULT hr = S_OK;

        hr = GetXREventTriggerBase(pIXRDO, &pEventTriggerBase);
        if (FAILED(hr))
        {
            goto Error;
        }

        if (pOldValue && SUCCEEDED(pOldValue->GetValue(&(oldSpec.pEventName))))
        {
            hr = pEventTriggerBase->GetSourceName(&(oldSpec.pSourceName));
            if (FAILED(hr))
            {
                goto Error;
            }
            hr = pEventTriggerBase->UnregisterHandler(&oldSpec);
            if (FAILED(hr))
            {
                goto Error;
            }
        }

        if (pNewValue && SUCCEEDED(pNewValue->GetValue(&(newSpec.pEventName))))
        {
            hr = pEventTriggerBase->GetSourceName(&(newSpec.pSourceName));
            if (FAILED(hr))
            {
                goto Error;
            }
            hr = pEventTriggerBase->RegisterHandler(&newSpec);
            if (FAILED(hr))
            {
                goto Error;
            }
        }

    Error:
        return;
    }

    /// <summary>
    /// Method used to handle a change to the SourceName dependency property.
    /// </summary>
    /// <param name="pIXRDO">IXRDependencyObject containing bridge object</param>
    /// <param name="pOldValue">XRValue containing the old value of SourceName</param>
    /// <param name="pNewValue">XRValue containing the new value of SourceName</param>
    static void CALLBACK SourceNameChanged(__in IXRDependencyObject* pIXRDO, __in XRValue* pOldValue, __in XRValue* pNewValue)
    {
        XREventTriggerSpecification oldSpec, newSpec;
        XRPtr<XREventTriggerBase> pEventTriggerBase;
        HRESULT hr = S_OK;

        hr = GetXREventTriggerBase(pIXRDO, &pEventTriggerBase);
        if (FAILED(hr))
        {
            goto Error;
        }

        if (pOldValue && SUCCEEDED(pOldValue->GetValue(&(oldSpec.pSourceName))))
        {
            hr = pEventTriggerBase->GetEventName(&(oldSpec.pEventName));
            if (FAILED(hr))
            {
                goto Error;
            }
            hr = pEventTriggerBase->UnregisterHandler(&oldSpec);
            if (FAILED(hr))
            {
                goto Error;
            }
        }

        if (pNewValue && SUCCEEDED(pNewValue->GetValue(&(newSpec.pSourceName))))
        {
            hr = pEventTriggerBase->GetEventName(&(newSpec.pEventName));
            if (FAILED(hr))
            {
                goto Error;
            }
            hr = pEventTriggerBase->RegisterHandler(&newSpec);
            if (FAILED(hr))
            {
                goto Error;
            }
        }
    
    Error:
        return;
    }

    /// <summary>
    /// Method used to handle a change to the Actions dependency property.
    /// </summary>
    /// <param name="pIXRDO">IXRDependencyObject containing bridge object</param>
    /// <param name="pOldValue">XRValue containing the old value of Actions item</param>
    /// <param name="pNewValue">XRValue containing the new value of Actions item</param>
    static void CALLBACK ActionsChanged(__in IXRDependencyObject* pIXRDO, __in XRValue* pOldValue, __in XRValue* pNewValue)
    {
        HRESULT hr = S_OK;
        XRPtr<IXRInteractivityTriggerActionCollection> pActionCollection;
        XRPtr<IXRDependencyObject> pAssociatedObject;
        XRPtr<XREventTriggerBase> pEventTriggerBase;
        XRPtr<IXRInteraction> pInteractions;
        XRPtr<TriggerImpl> pEventTriggerImpl;

        if (!pIXRDO || !pOldValue || !pNewValue)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        // if there is an old value, detach it
        if (pOldValue->vType == VTYPE_ENUMERABLE && pOldValue->pEnumerableVal)
        {
            pActionCollection = pOldValue->pEnumerableVal;
            if (!pActionCollection)
            {
                hr = E_NOTIMPL;
                goto Error;
            }

            hr = pActionCollection->OnDetaching();
            if (FAILED(hr))
            {
                goto Error;
            }
        }
        // if there is a new value, attach it
        if (pNewValue->vType == VTYPE_ENUMERABLE && pNewValue->pEnumerableVal)
        {
            hr = GetXREventTriggerBase(pIXRDO, &pEventTriggerBase);
            if (FAILED(hr))
            {
                goto Error;
            }

            pActionCollection = pNewValue->pEnumerableVal;
            if (!pActionCollection)
            {
                hr = E_NOTIMPL;
                goto Error;
            }

            hr = pEventTriggerBase->GetTargetObject(&pAssociatedObject);
            if (FAILED(hr))
            {
                goto Error;
            }
            hr = pActionCollection->OnAttached(pAssociatedObject);
            if (FAILED(hr))
            {
                goto Error;
            }
        }

    Error:
        return;
    }

private:
    XRPtr<IXRDelegate<XRRoutedEventArgs>> m_pLoadedEventHandler;

public:
    static UINT s_ControlId;
    static DEPENDENCY_PROPERTY_ID s_eventNameId;
    static DEPENDENCY_PROPERTY_ID s_sourceNameId;
    static DEPENDENCY_PROPERTY_ID s_actionsId;
};
//
// Initialize static variables
template <class ConstraintType, class TriggerImpl, class IFace>
UINT XREventTriggerBase<ConstraintType, TriggerImpl, IFace>::s_ControlId = 0;
template <class ConstraintType, class TriggerImpl, class IFace>
DEPENDENCY_PROPERTY_ID XREventTriggerBase<ConstraintType, TriggerImpl, IFace>::s_eventNameId;
template <class ConstraintType, class TriggerImpl, class IFace>
DEPENDENCY_PROPERTY_ID XREventTriggerBase<ConstraintType, TriggerImpl, IFace>::s_sourceNameId;
template <class ConstraintType, class TriggerImpl, class IFace>
DEPENDENCY_PROPERTY_ID XREventTriggerBase<ConstraintType, TriggerImpl, IFace>::s_actionsId;


//
// templated base class for IActions
//
template <typename ActionImpl, typename IFace, typename ConstraintType = IXRDependencyObject>
class XRAction : 
    public XRAttachedObject<ConstraintType, IFace>
{
    BEGIN_QI_MAP()
        QI_MAP_CHAIN(XRAttachedObject)
        QI_MAP_INTERFACE(ActionImpl)
        QI_MAP_INTERFACE(IXRInteractivityTriggerAction)
    END_QI_MAP()

public:
    /// <summary>
    /// Method used to create a new Action
    /// </summary>
    /// <param name="pDependencyObject">IXRDependencyObject containing corresponding XamlRuntime object</param>
    /// <param name="ppAttachedObject">IXRAttachedObject storing the newly created object</param>
    static HRESULT CALLBACK Create(
        __in IXRDependencyObject *pDependencyObject, 
        __out IXRAttachedObject **ppAttachedObject)
    {
        return XRAttachedObject::Create<ActionImpl>(pDependencyObject, ppAttachedObject);
    }

    /// <summary>
    /// Method used to register a new Action
    /// </summary>
    /// <param name="pActionName">string containing the XAML name of this action</param>
    /// <param name="pActionNamespace">string containing the namespace of this action</param>
    static HRESULT Register(
        __in const WCHAR *pActionName, 
        __in const WCHAR *pActionNamespace)
    {
        XRPtr<IXRInteraction> pInteractions;
        HRESULT hr = S_OK;

        hr = GetInteractionsInstance(&pInteractions);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = pInteractions->RegisterAttachedObject(
                __uuidof(IFace), 
                pActionName, pActionNamespace,
                Create, &s_ActionId);
        if (FAILED(hr))
        {
            goto Error;
        }

    Error:
        return hr;
    }

protected:
    static UINT s_ActionId;
};
template <typename ActionImpl, typename IFace, typename ConstraintType>
UINT XRAction<ActionImpl, IFace, ConstraintType>::s_ActionId = 0;

//
// templated baseclass for targeted actions
//
template <typename ActionImpl, typename IFace, typename ConstraintType = IXRDependencyObject>
class XRTargetedAction : public XRAction<ActionImpl, IFace, ConstraintType>
{
public:
    /// <summary>
    /// Method used to get the target name
    /// </summary>
    /// <param name="pTargetName">string containing the target's name</param>
    HRESULT STDMETHODCALLTYPE GetTargetName(__out BSTR *pTargetName)
    {
        return XRAttachedObject::GetDependencyProperty(s_TargetNameId, pTargetName);
    }
    /// <summary>
    /// Method used to set the target name
    /// </summary>
    /// <param name="pTargetName">string containing the target's name</param>
    HRESULT STDMETHODCALLTYPE SetTargetName(__in const WCHAR* pTargetName)
    {
        return XRAttachedObject::SetDependencyProperty(s_TargetNameId, pTargetName);
    }

public:
    /// <summary>
    /// Method used to register a new Action
    /// </summary>
    /// <param name="pActionName">string containing the XAML name of this action</param>
    /// <param name="pActionNamespace">string containing the namespace of this action</param>
    static HRESULT Register(
        __in const WCHAR *pActionName, 
        __in const WCHAR *pActionNamespace)
    {
        XRPtr<IXRApplication> pApplication;
        HRESULT hr = S_OK;

        hr = XRAction::Register(pActionName, pActionNamespace);
        if (FAILED(hr))
        {
            goto Error;
        }
        hr = GetXRApplicationInstance(&pApplication);
        if (FAILED(hr))
        {
            goto Error;
        }
        {
            XRDependencyPropertyMetaData TargetNameMd;
            hr = pApplication->RegisterDependencyProperty(
                    L"TargetName", VTYPE_BSTR, s_ActionId, 
                    &TargetNameMd, &s_TargetNameId);
            if (FAILED(hr))
            {
                goto Error;
            }
        }

    Error:
        return hr;
    }

    /// <summary>
    /// Method used to get the action's target.  Makes use of the TargetName property and the 
    /// attached object.
    /// </summary>
    /// <param name="ppElement">IXRFrameworkElement used to store the action's target</param>
    HRESULT STDMETHODCALLTYPE GetTarget(__out IXRFrameworkElement **ppElement)
    {
        XRPtr<IXRDependencyObject> pAssociatedObject;
        XRPtr<IXRFrameworkElement> pTarget;
        XRPtr<IXRFrameworkElement> pRoot;
        XRPtr<IXRFrameworkElement> pParentFE;
        XRPtr<IXRCustomUserControl> pParentCUC;
        ce::auto_bstr pTargetName;
        HRESULT hr = S_OK;
        size_t length = 0;

        if (NULL == ppElement)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        // start by clearing parameter
        *ppElement = NULL;

        // get target  object
        hr = XRAttachedObject::GetTargetObject(&pAssociatedObject);
        if (FAILED(hr))
        {
            goto Error;
        }
        // ensure that this action is attached
        if (NULL == pAssociatedObject)
        {
            hr = E_FAIL;
            goto Error;
        }

        // get the target name
        hr = GetTargetName(&pTargetName);
        if (FAILED(hr))
        {
            goto Error;
        }
        if (NULL != pTargetName)
        {
            hr = StringCchLength(pTargetName, MAX_INTERACTION_STRING_LENGTH, &length);
            if (FAILED(hr))
            {
                goto Error;
            }
        }
       
        if (0 < length)
        {
            // start with the associated object
            pRoot = pAssociatedObject;
            pParentFE = pAssociatedObject;
            pParentCUC = pAssociatedObject;

            // if the associated object is a framework element, walk up 
            // the tree until the first UserControl or the root is found
            // pParentFE equals null when 
            //    pRoot points to the topmost object in the visual tree
            while (pRoot && !pParentCUC && pParentFE)
            {
                // GetParent will return S_OK when parent object is not found
                if (SUCCEEDED(pRoot->GetParent(&pParentFE)))
                {
                    if (pParentFE)
                    {
                        pRoot = pParentFE;
                        pParentCUC = pParentFE;
                    }
                }
            }

            // ensure that the user control exists
            if (NULL == pRoot)
            {
                hr = E_FAIL;
                goto Error;
            }
            
            // use root to find an element with the target's name
            hr = pRoot->FindName(pTargetName, &pTarget);
            if (FAILED(hr))
            {
                goto Error;
            }
        }
        else
        {
            // no target name was specified...use the associated object
            pTarget = pAssociatedObject;
        }

        if (NULL == pTarget)
        {
            hr = E_FAIL;
            goto Error;
        }
        *ppElement = pTarget.Detach();

    Error:
        return hr;
    }

    // Get the target object.
    //  
    // This typesafe version will return the value only if the
    // destination interface (ppTargetObject) matches the 
    // target element.
    // 
    template<typename XRObj> HRESULT GetTarget(__out XRObj** ppTarget)
    {
        XRPtr<IXRFrameworkElement> pTemp;

        HRESULT hr = GetTarget(&pTemp);

        if(SUCCEEDED(hr) && pTemp)
        {
            hr = pTemp.QueryInterface(ppTarget);
        }

        return hr;
    }
private:
    static DEPENDENCY_PROPERTY_ID s_TargetNameId;
};
template <typename ActionImpl, typename IFace, typename ConstraintType>
DEPENDENCY_PROPERTY_ID XRTargetedAction<ActionImpl, IFace, ConstraintType>::s_TargetNameId;



//
// MACROs used to simplify property declaration, registry, and definition
//
//
//


//
// Register a property
//
#define REGISTER_PROPERTY(Type, PropertyName, ValueType) \
    {\
        XRDependencyPropertyMetaData md_##PropertyName##Id;\
        hr = pApplication->RegisterDependencyProperty(\
                        L#PropertyName, ValueType, \
                        s_##Type##Id, &md_##PropertyName##Id, \
                        &s_##PropertyName##Id);\
        if (FAILED(hr)) \
        { \
            goto Error; \
        } \
    }\

//
// Register a property with a specified default value
//
#define REGISTER_PROPERTY_WITH_DEFAULT(Type, PropertyName, ValueType, PropertyDefaultValue) \
        {\
            XRDependencyPropertyMetaData md_##PropertyName##Id;\
            md_##PropertyName##Id.DefaultValue = PropertyDefaultValue;\
            hr = pApplication->RegisterDependencyProperty(\
                            L#PropertyName, ValueType, \
                            s_##Type##Id, &md_##PropertyName##Id, \
                            &s_##PropertyName##Id);\
            if (FAILED(hr)) \
            { \
                goto Error; \
            } \
        }\

//
// Register a property with a specified type converter
//
#define REGISTER_PROPERTY_WITH_TYPE_CONVERTER(Type, PropertyName, ValueType, TypeConverter) \
        {\
            XRDependencyPropertyMetaData md_##PropertyName##Id;\
            md_##PropertyName##Id.pfnTypeConverter = TypeConverter;\
            hr = pApplication->RegisterDependencyProperty(\
                            L#PropertyName, ValueType, \
                            s_##Type##Id, &md_##PropertyName##Id, \
                            &s_##PropertyName##Id);\
            if (FAILED(hr)) \
            { \
                goto Error; \
            } \
        }\

//
// Register a property with a specified change handler
//
#define REGISTER_PROPERTY_WITH_CHANGE_HANDLER(Type, PropertyName, ValueType, ValueChangeHandler) \
    {\
        XRDependencyPropertyMetaData md_##PropertyName##Id;\
        md_##PropertyName##Id.pfnPropertyChangeNotification = ValueChangeHandler;\
        hr = pApplication->RegisterDependencyProperty(\
                        L#PropertyName, ValueType, \
                        s_##Type##Id, &md_##PropertyName##Id, \
                        &s_##PropertyName##Id);\
        if (FAILED(hr)) \
        { \
            goto Error; \
        } \
    }\

//
// Register a property with metadata specified
//
#define REGISTER_PROPERTY_WITH_METADATA(Type, PropertyName, ValueType, Metadata) \
    {\
        hr = pApplication->RegisterDependencyProperty(\
                        L#PropertyName, ValueType, \
                        s_##Type##Id, &Metadata, \
                        &s_##PropertyName##Id);\
        if (FAILED(hr)) \
        { \
            goto Error; \
        } \
    }\

//
// Macro used to declare a Primitive type (int, float, bool, etc) Property 
//      HRESULT STDMETHODCALLTYPE SetFoo(int Value);
//      HRESULT STDMETHODCALLTYPE GetFoo(int *pValue);
//
#define DECLARE_PRIMITIVE_PROPERTY(PropertyName, ValueType) \
public:\
    HRESULT STDMETHODCALLTYPE Set##PropertyName(__in ValueType Value);\
    HRESULT STDMETHODCALLTYPE Get##PropertyName(__out ValueType *pValue);\
private:\
    static DEPENDENCY_PROPERTY_ID s_##PropertyName##Id;\

#define DEFINE_PRIMITIVE_PROPERTY(ClassName, PropertyName, ValueType) \
DEPENDENCY_PROPERTY_ID ClassName::s_##PropertyName##Id;\
HRESULT ClassName::Set##PropertyName(__in ValueType Value)\
{\
    return XRAttachedObject::SetDependencyProperty(s_##PropertyName##Id, Value);\
}\
HRESULT ClassName::Get##PropertyName(__out ValueType *pValue)\
{\
    return XRAttachedObject::GetDependencyProperty(s_##PropertyName##Id, pValue);\
}\

//
// Macro used to declare a struct type Property 
//      HRESULT STDMETHODCALLTYPE SetFoo(fooStruct *pValue);
//      HRESULT STDMETHODCALLTYPE GetFoo(fooStruct *pValue);
//
#define DECLARE_STRUCT_PROPERTY(PropertyName, ValueType) \
public:\
    HRESULT STDMETHODCALLTYPE Set##PropertyName(__in ValueType *pValue);\
    HRESULT STDMETHODCALLTYPE Get##PropertyName(__out ValueType *pValue);\
private:\
    static DEPENDENCY_PROPERTY_ID s_##PropertyName##Id;\

#define DEFINE_STRUCT_PROPERTY(ClassName, PropertyName, ValueType) \
DEPENDENCY_PROPERTY_ID ClassName::s_##PropertyName##Id;\
HRESULT ClassName::Set##PropertyName(__in ValueType *pValue)\
{\
    return XRAttachedObject::SetDependencyProperty(s_##PropertyName##Id, pValue);\
}\
HRESULT ClassName::Get##PropertyName(__out ValueType *pValue)\
{\
    return XRAttachedObject::GetDependencyProperty(s_##PropertyName##Id, pValue);\
}\

//
// Macro used to declare a class type Property 
//      HRESULT STDMETHODCALLTYPE SetFoo(fooClass *pValue);
//      HRESULT STDMETHODCALLTYPE GetFoo(fooClass **ppValue);
//
#define DECLARE_CLASS_PROPERTY(PropertyName, ValueType) \
public:\
    HRESULT STDMETHODCALLTYPE Set##PropertyName(__in ValueType *pValue);\
    HRESULT STDMETHODCALLTYPE Get##PropertyName(__out ValueType **ppValue);\
private:\
    static DEPENDENCY_PROPERTY_ID s_##PropertyName##Id;\

#define DEFINE_CLASS_PROPERTY(ClassName, PropertyName, ValueType) \
DEPENDENCY_PROPERTY_ID ClassName::s_##PropertyName##Id;\
HRESULT ClassName::Set##PropertyName(__in ValueType *pValue)\
{\
    return XRAttachedObject::SetDependencyProperty(s_##PropertyName##Id, pValue);\
}\
HRESULT ClassName::Get##PropertyName(__out ValueType **ppValue)\
{\
    return XRAttachedObject::GetDependencyProperty(s_##PropertyName##Id, ppValue);\
}\


//
// Macro used to declare a string type Property 
//      HRESULT STDMETHODCALLTYPE SetFoo(const WCHAR *pValue);
//      HRESULT STDMETHODCALLTYPE GetFoo(BSTR *pValue);
//
#define DECLARE_STRING_PROPERTY(PropertyName) \
public:\
    HRESULT STDMETHODCALLTYPE Set##PropertyName(__in const WCHAR *pValue);\
    HRESULT STDMETHODCALLTYPE Get##PropertyName(__out BSTR *pValue);\
private:\
    static DEPENDENCY_PROPERTY_ID s_##PropertyName##Id;\

#define DEFINE_STRING_PROPERTY(ClassName, PropertyName) \
DEPENDENCY_PROPERTY_ID ClassName::s_##PropertyName##Id;\
HRESULT ClassName::Set##PropertyName(__in const WCHAR *pValue)\
{\
    return XRAttachedObject::SetDependencyProperty(s_##PropertyName##Id, pValue);\
}\
HRESULT ClassName::Get##PropertyName(__out BSTR *pValue)\
{\
    return XRAttachedObject::GetDependencyProperty(s_##PropertyName##Id, pValue);\
}\


